/*
 * SIT_App.c : root of all widget hierarchy.
 *
 * Written by T.Pierron, Feb 27, 2006.
 */

#include "SIT_P.h"
#include "graphics_P.h"

	TagList AppClass[] = {
		{ SIT_UniqueApp,   "uniqueApp",   _SG, SIT_BOOL, OFFSET(SIT_App, sa_UniqueApp) },
		{ SIT_LocaleInfo,  "localeInfo",  __G, SIT_PTR,  OFFSET(SIT_App, sa_Locale) },
		{ SIT_ClipFormats, "clipFormats", __G, SIT_PTR,  OFFSET(SIT_App, sa_Formats) },
		{ SIT_CurrentDir,  "currentDir",  _SG, SIT_PTR,  OFFSET(SIT_App, sa_CurrentDir) },
		{ SIT_TagEnd }
	};

static int SIT_SetAppValues(SIT_Widget w, APTR call_data, APTR user_data)
{
	TagList *     tag   = call_data;
	SIT_Variant * value = user_data;

	switch (tag->tl_TagID) {
	case SIT_UniqueApp:
		app->sa_UniqueApp = value->boolean;
		if (app->sa_UniqueApp && ! app->sa_Mapping)
		{
			BOOL first;
			app->sa_Mapping = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0,
				sizeof (*app->sa_Data), w->sw_Name);
			first = (GetLastError() == 0);
			app->sa_Data = MapViewOfFile(app->sa_Mapping, FILE_MAP_WRITE, 0, 0, 0);

			if (first)
			{
				app->sa_Data->threadid = GetCurrentThreadId();
				app->sa_Data->argc = 0;
			}
			else
			{
				/* Pre parse command line */
				int      remain, i, nb;
				LPWSTR * argv = CommandLineToArgvW(GetCommandLine(), &nb);
				STRPTR   p = app->sa_Data->argv;

				for (i = 0, remain = sizeof app->sa_Data->argv; i < nb; i ++)
				{
					int len = WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, p, remain, NULL, 0);

					if (len > 0) remain -= len, p += len;
					else break;
				}
				LocalFree(argv);
				app->sa_Data->argc = i;
				PostThreadMessage(app->sa_Data->threadid, WM_NEWINSTANCE, 0, 0);
				app->sa_Ret = False;
			}
		}
		else if (! app->sa_UniqueApp && app->sa_Mapping)
		{
			if (app->sa_Data) UnmapViewOfFile(app->sa_Data);
			CloseHandle(app->sa_Mapping);
			app->sa_Data = NULL;
			app->sa_Mapping = NULL;
		}
		break;
	case SIT_LocaleInfo:
	case SIT_ClipFormats:
		return 0;
	case SIT_CurrentDir:
		/* Note: value->string is a user-supplied buffer, not a copy. Do not modify */
		if (value->string)
		{
			LPWSTR str;
			UTF8ToUTF16(value->string, str);
			SetCurrentDirectory(str);
		}
	}
	return 0;
}

static LocaleInfo SIT_AllocLocale(void)
{
	static short locale[] = { /* Stuff we'll extract from GetLocaleInfo */
		LOCALE_SNATIVELANGNAME,    LOCALE_SNATIVECTRYNAME,    LOCALE_SENGLANGUAGE,
		LOCALE_SENGCOUNTRY,        LOCALE_SISO3166CTRYNAME,   LOCALE_SISO639LANGNAME,
		LOCALE_SCURRENCY,          LOCALE_SENGCURRNAME,       LOCALE_SINTLSYMBOL,
		LOCALE_SSHORTDATE,         LOCALE_SLONGDATE,          LOCALE_SDECIMAL,
		LOCALE_SMONTHNAME1,        LOCALE_SMONTHNAME2,        LOCALE_SMONTHNAME3,
		LOCALE_SMONTHNAME4,        LOCALE_SMONTHNAME5,        LOCALE_SMONTHNAME6,
		LOCALE_SMONTHNAME7,        LOCALE_SMONTHNAME8,        LOCALE_SMONTHNAME9,
		LOCALE_SMONTHNAME10,       LOCALE_SMONTHNAME11,       LOCALE_SMONTHNAME12,
		LOCALE_SABBREVMONTHNAME1,  LOCALE_SABBREVMONTHNAME2,  LOCALE_SABBREVMONTHNAME3,
		LOCALE_SABBREVMONTHNAME4,  LOCALE_SABBREVMONTHNAME5,  LOCALE_SABBREVMONTHNAME6,
		LOCALE_SABBREVMONTHNAME7,  LOCALE_SABBREVMONTHNAME8,  LOCALE_SABBREVMONTHNAME9,
		LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
		LOCALE_SDAYNAME1,          LOCALE_SDAYNAME2,          LOCALE_SDAYNAME3,
		LOCALE_SDAYNAME4,          LOCALE_SDAYNAME5,          LOCALE_SDAYNAME6,
		LOCALE_SDAYNAME7,          LOCALE_SABBREVDAYNAME1,    LOCALE_SABBREVDAYNAME2,
		LOCALE_SABBREVDAYNAME3,    LOCALE_SABBREVDAYNAME4,    LOCALE_SABBREVDAYNAME5,
		LOCALE_SABBREVDAYNAME6,    LOCALE_SABBREVDAYNAME7,    LOCALE_ILANGUAGE,
		LOCALE_ICOUNTRY,           LOCALE_STIMEFORMAT
	};
	LPWSTR buffer, p;
	STRPTR d, * s;
	int    i, j, len;

	for (i = len = 0; i < DIM(locale); len += GetLocaleInfo(LOCALE_USER_DEFAULT,
	     locale[i], NULL, 0), i ++);

	buffer = alloca(len * sizeof *buffer);

	for (p = buffer, i = j = len = 0; i < DIM(locale); i ++, p += j) {
		j = GetLocaleInfo(LOCALE_USER_DEFAULT, locale[i], p, 0xffff);
		len += WideCharToMultiByte(CP_UTF8, 0, p, -1, NULL, 0, NULL, NULL);
		if (locale[i] == LOCALE_SISO639LANGNAME) p[-1] = '_';
	}
	LocaleInfo loc = malloc(sizeof *loc + len);

	for (s = &loc->nlsLang, d = (STRPTR) (loc+1), p = buffer, i = 0; i < DIM(locale) - 1; i ++, s ++) {
		d += WideCharToMultiByte(CP_UTF8, 0, p, -1, *s = d, len, NULL, NULL);
		p = wcschr(p, 0) + 1;
	}
	return loc;
}

static uint8_t maskbit[] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};

/* Create an icon from a graphics:Image (use to display icon on title bar of dialogs) */
HICON SIT_IconFromBitmap(Image img)
{
	if (0 < img && img < (Image) 0x10000)
	{
		/* Hack(TM): resource identifier */
		return LoadIcon(instance, (LPWSTR) img);
	}
	if (img == NULL || img->bpp != 32) return 0;
	BITMAPINFOHEADER bmp = {
		.biSize = sizeof bmp, .biWidth = img->width, .biHeight = img->height*2, .biPlanes = 1,
		.biBitCount = 32, .biCompression = BI_RGB, .biSizeImage = img->height * img->stride
	};
	int   mask = (img->width/8+3) & ~3;
	DATA8 mem = malloc(bmp.biSizeImage + mask * img->height + sizeof bmp);
	DATA8 s, d, m, a;
	int   i, j;
	memcpy(mem, &bmp, sizeof bmp);
	/* Convert alpha to 1bit mask */
	for (s = img->bitmap + bmp.biSizeImage - img->stride, d = mem + sizeof bmp, m = d + bmp.biSizeImage, i = img->height; i > 0;
	     i --, s -= img->stride, d += img->stride, m += mask)
	{
		memcpy(d, s, img->stride);
		/* Setup mask (backward compatibility only, newer version of windows should rely on alpha mask alone) */
		for (a = s + 3, j = 0; j < img->width; j ++, a += 4)
			if (*a >= 128) m[j&3] |= maskbit[j&3];
	}

	HICON ret = CreateIconFromResourceEx(mem, bmp.biSizeImage + sizeof bmp, TRUE, 0x00030000, img->width, img->height, LR_DEFAULTCOLOR);
	free(mem);
	return ret;
}

/* Custom cursor shape */
HCURSOR SIT_ImageToCursor(Image img)
{
	int w = GetSystemMetrics(SM_CXCURSOR);
	int h = GetSystemMetrics(SM_CYCURSOR);
	int sz = (w+7) >> 3, i = sz*h, j, k;
	DATA8 andmask = alloca(i), da, dx;
	DATA8 xormask = alloca(i), p;

	memset(andmask, 0xff, i);
	memset(xormask, 0,    i);
	for (j = MIN(h, img->height), da = andmask, dx = xormask, p = img->bitmap; j > 0; j --, da += sz, dx += sz, p += img->stride)
	{
		for (i = MIN(w, img->width)-1, k = 0; i >= 0; i --, k ++)
		{
			int px = p[k>>1];
			if (k&1) px &= 3; else px >>= 2;
			switch (px) {
			case 3: // no break; reverse: and = 1, xor = 1
			case 2: dx[k>>3] |= maskbit[k&7]; // white: xor = 1, and = 0
			        if (px == 3) break;
			case 1: da[k>>3] ^= maskbit[k&7]; break; // black: xor = and = 0
			//default: transparent and = 1, xor = 0
			}
		}
	}
	return CreateCursor(instance, img->encoder & 0xff, img->encoder>>8, w, h, andmask, xormask);
}


BOOL SIT_CreateApp(SIT_Widget w, va_list args)
{
	/* It is a global variable. Can't be pre-allocated in SIT_Init() */
	app = (SIT_App) w;

	app->sa_Ret = True;
	/* There is no finalize, because, well, it only has to be free()'ed on exit */
	w->sw_SetValue = SIT_SetAppValues;

	SIT_ParseTags(w, args, w->sw_Class = AppClass);

	app->sa_Locale = SIT_AllocLocale();

	return app->sa_Ret;
}

/* Instance notification */
void SIT_AppNotify(SIT_App app)
{
	if (app && HAS_EVT(&app->super, SITE_OnActivate) && app->sa_Data)
	{
		int      argc = app->sa_Data->argc, i;
		STRPTR   str  = app->sa_Data->argv;
		STRPTR * argv = alloca((argc + 1) * sizeof (STRPTR));

		for (i = 0; i < argc; argv[i] = str, str = strchr(str, 0) + 1, i ++);
		argv[i] = NULL;
		SIT_ApplyCallback(&app->super, argv, SITE_OnActivate);
	}
}

/* Gather list of window class in a global list */
BOOL SIT_RegisterClass(SIT_Widget w, LPWSTR clsName, int flags, LPWSTR arrow, APTR icon)
{
	WNDCLASSEX wcex;
	HBRUSH     bg;
	ULONG      bgcol = 0;
	TEXT       ch = '0';

	if (w->sw_Flags & SITF_HasCustBg)
	{
		bgcol = w->sw_Bg;
		ch = '1';
		if (ISSYS(bgcol)) bg = (HBRUSH) (sysColorsWin32[bgcol & 0xffff] + 1);
		else bg = CreateSolidBrush(bgcol);
	}
	else bg = (HBRUSH) (COLOR_BTNFACE+1);

	if (app && app->sa_UniqueApp)
		wcsncpy(clsName, app->super.sw_Name, 32), clsName[31] = 0, app->sa_UniqueApp = False;
	else
		wsprintf(clsName, L"SIT_%c%08lx_%x_%p", ch, bgcol, flags, arrow);

	if (icon) wsprintf(wcschr(clsName, 0), L"_%p", icon);

	/* We are first instance, register class */
	memset(&wcex, 0, sizeof wcex);

	wcex.cbSize        = sizeof wcex;
	wcex.style         = flags;
	wcex.lpfnWndProc   = SIT_SubClassHandler;
	wcex.cbClsExtra    = sizeof icon;
	wcex.hInstance     = instance;
	wcex.hCursor       = LoadCursor(NULL, arrow);
	wcex.hbrBackground = w->sw_BgBrush = bg;
	wcex.lpszClassName = clsName;

	/* Usually call fails if class with same name already exists */
	return RegisterClassEx(&wcex);
}

static void CALLBACK SIT_TimerHook(HWND wnd, UINT message, UINT timerID, DWORD time)
{
	SIT_Widget w = (SIT_Widget) GetProp(wnd, sit_class);
	SIT_Timer t, next;

	for (t = next = HEAD(app->sa_Timers); t; t = next)
	{
		SNEXT(next, node);
		if (t->timerid == timerID && t->cb(w, (APTR) time, t->ud) == 0)
		{
			/* Stop the timer */
			ListRemove(&app->sa_Timers, &t->node);
			KillTimer(wnd, timerID);
			free(t);
		}
	}
}

DLLIMP int SIT_SetTimer(SIT_Widget w, SIT_CallProc cb, APTR data, int msec)
{
	static int timerId = 0;

	SIT_Timer timer = calloc(sizeof *timer, 1);

	if (timer == NULL) return 0;

	timer->w = w;
	timer->timerid = ++ timerId;
	timer->cb = cb;
	timer->ud = data;
	ListAddTail(&app->sa_Timers, &timer->node);

	SetTimer(w->sw_Handle, timerId, msec, SIT_TimerHook);

	return timerId;
}

DLLIMP void SIT_StopTimer(int timerid)
{
	SIT_Timer timer;

	for (timer = HEAD(app->sa_Timers); timer; NEXT(timer))
	{
		if (timer->timerid == timerid) {
			KillTimer(timer->w->sw_Handle, timerid);
			ListRemove(&app->sa_Timers, &timer->node);
			free(timer);
			return;
		}
	}
}
